package com.tarena.charging.device.service;

import cn.tedu.charging.common.pojo.JsonResult;
import cn.tedu.charging.common.pojo.param.GunStatusUpdateParam;
import cn.tedu.charging.common.pojo.vo.StationInfoVO;
import com.alibaba.nacos.common.utils.CollectionUtils;
import com.alibaba.nacos.shaded.org.checkerframework.checker.units.qual.C;
import com.tarena.charging.device.po.StationPO;
import com.tarena.charging.device.repository.GunRepository;
import com.tarena.charging.device.repository.PileRepository;
import com.tarena.charging.device.repository.StationRepository;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.Circle;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.GeoResult;
import org.springframework.data.geo.Metric;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.GeoOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;

@Service
@Slf4j
public class DeviceService {
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private StationRepository stationRepository;
    //利用gunId读取station详情 需要使用 另外2个数据层
    @Autowired
    private GunRepository gunRepository;
    @Autowired
    private PileRepository pileRepository;
    public List<StationInfoVO> getNearStation(Double longitude, Double latitude, Double distance) {
        GeoOperations geoOps = redisTemplate.opsForGeo();
        String stationKey="charging_geo";
        //1.封装 georadius 参数 circle
        Circle circle=assembleCircle(longitude, latitude, distance);
        //2.封装选项
        RedisGeoCommands.GeoRadiusCommandArgs args=withCoordAndDist();
        //3.调用查询 并不需要平均距离 所以直接接收list
        List<GeoResult<RedisGeoCommands.GeoLocation<String>>>
            members = geoOps.radius(stationKey, circle, args).getContent();
        //4.判断是否是空 如果是空 无需封装vo 如果不是空需要封装vo
        List<StationInfoVO> vos=assembleVOS(members);
        return vos;
    }

    private RedisGeoCommands.GeoRadiusCommandArgs withCoordAndDist() {
        //新建一个参数对象
        RedisGeoCommands.GeoRadiusCommandArgs args=
            RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeCoordinates().includeDistance();
        return args;
    }

    private Circle assembleCircle(Double longitude, Double latitude, Double distance) {
        //Point中心点
        Point center=new Point(longitude,latitude);
        //Distance半径
        Distance radius=new Distance(distance);
        return new Circle(center,radius);

    }

    private List<StationInfoVO> assembleVOS(List<GeoResult<RedisGeoCommands.GeoLocation<String>>> members) {
        List<StationInfoVO> vos;
        if (CollectionUtils.isNotEmpty(members)){
            //使用steram()流api映射map封装vos
            vos=members.stream().map(member->{
                StationInfoVO vo=new StationInfoVO();
                vo.setStationName(member.getContent().getName());//String类型数据 存储在redis就是元素值
                vo.setStationLng(BigDecimal.valueOf(member.getContent().getPoint().getX()));//x坐标
                vo.setStationLat(BigDecimal.valueOf(member.getContent().getPoint().getY()));//y坐标
                vo.setDistance(member.getDistance().getValue());//distance.value
                //充电站名称 独一无二的 可以查询
                Integer id=stationRepository.getIdByName(vo.getStationName());
                vo.setId(id);//没法从redis返回结果中拿到需要读数据库
                return vo;
            }).collect(Collectors.toList());

            /*for (GeoResult<RedisGeoCommands.GeoLocation<String>>
                member : members) {
                //menber包含 元素值 坐标值 和距离
                //创建vo 填补属性
                StationInfoVO vo=new StationInfoVO();
                vo.setStationName(member.getContent().getName());//String类型数据 存储在redis就是元素值
                vo.setStationLng(BigDecimal.valueOf(member.getContent().getPoint().getX()));//x坐标
                vo.setStationLat(BigDecimal.valueOf(member.getContent().getPoint().getY()));//y坐标
                vo.setDistance(member.getDistance().getValue());//distance.value
                //充电站名称 独一无二的 可以查询
                Integer id=stationRepository.getIdByName(vo.getStationName());
                vo.setId(id);//没法从redis返回结果中拿到需要读数据库
                vos.add(vo);
            }*/
        } else {
            vos = null;
        }
        return vos;
    }

    public StationInfoVO getStationInfo(Integer gunId) {
        // 缓存数据 可以使用hash 也可以使用string类型
        ValueOperations stringOps = redisTemplate.opsForValue();//操作 key-value string类型数据对象
        // 1. 设计一个缓存的key值 业务前缀+业务数据 业务前缀描述当前业务 业务数据 缓存关联的id name
        String stationKey="station:by:gun:id:"+gunId;
        // 读取缓存
        StationInfoVO vo= (StationInfoVO) stringOps.get(stationKey);
        // 2.判断空
        if(vo==null){
            log.info("当前缓存没命中");
            //缓存没命中
            Integer pileId=gunRepository.getPiledByGunId(gunId);
            Integer stationId=pileRepository.getStationByPileId(pileId);
            StationPO po=stationRepository.getById(stationId);
            vo=new StationInfoVO();
            BeanUtils.copyProperties(po,vo);
            stringOps.set(stationKey,vo);
            // 充电枪的id查询 pile_id 充电桩id select pile_id from charging_gun_info where id=#{gunId};
            // 充电桩id 查询 station_id select station_id from charging_pile_info where id=#{pile_id}
            // 查询充电站详情 select * from charging_station where id=#{station_id}
            // 当前业务接口 为2c功能准备 的 尽可能 避免关联查询
            // 但是每次都查询3个sql 才能后去到需要的信息 数据库压力也不小
            // 添加缓存逻辑
        }
        return vo;
    }

    public Boolean updateGunStatus(GunStatusUpdateParam param) {
        int row=gunRepository.updateGunStatus(param);//row的返回值是myabtis框架封装的 执行写操作的影响行数
        return row==1;
    }
}
